

package com.kimo.shop.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.PutObjectRequest;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kimo.shop.common.exception.KimoShopBindException;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.region.Region;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import com.kimo.shop.bean.model.AttachFile;
import com.kimo.shop.bean.model.AttachFileGroup;
import com.kimo.shop.common.bean.AliOss;
import com.kimo.shop.common.bean.QCloud;
import com.kimo.shop.common.bean.Qiniu;
import com.kimo.shop.common.enums.QiniuZone;
import com.kimo.shop.common.util.PageParam;
import com.kimo.shop.config.ShopConfig;
import com.kimo.shop.dao.AttachFileMapper;
import com.kimo.shop.service.AttachFileGroupService;
import com.kimo.shop.service.AttachFileService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author kimo-taozhouchuan
 * @date 2018/07/27
 */
@Service
@AllArgsConstructor
@SuppressWarnings("deprecation")
public class AttachFileServiceImpl extends ServiceImpl<AttachFileMapper, AttachFile> implements AttachFileService {

    private final AttachFileMapper attachFileMapper;

    private final ShopConfig shopConfig;

    private final AttachFileGroupService attachFileGroupService;

    public final static String NORM_MONTH_PATTERN = "yyyy/MM/";

    @Override
    public IPage<AttachFile> getPage(PageParam<AttachFile> page, AttachFile attachFile) {
        return attachFileMapper.getPage(page, attachFile);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String uploadFile(byte[] bytes, String originalName) throws IOException {
        String extName = FileUtil.extName(originalName);
        String imgName = FileUtil.mainName(originalName);
        String filePath = randomFilePath(extName);
        AttachFile attachFile = getAttachFile(bytes, extName, imgName, filePath);
        attachFileMapper.insert(attachFile);
        upload(bytes, filePath, extName);
        return filePath;
    }

    @Override
    public Long uploadImg(byte[] bytes, AttachFile attachFile, String extName) throws IOException {
        String filePath = randomFilePath(attachFile.getFileType());
        //数据存入数据库
        attachFile.setFilePath(filePath);
        attachFile.setFileSize(bytes.length);
        attachFileMapper.insert(attachFile);
        // 上传
        upload(bytes, filePath, extName);
        return attachFile.getFileId();
    }

    private void upload(byte[] bytes, String filePath, String extName) throws IOException {
        Qiniu qiniu = shopConfig.getQiniu();
        if (qiniu.getIsOpen()) {
            String upToken = auth(qiniu).uploadToken(qiniu.getBucket(), filePath);
            uploadManager(qiniu).put(bytes, filePath, upToken);
            return;
        }

        AliOss aliOss = shopConfig.getAliOss();
        if (aliOss.getIsOpen()) {
            OSS ossClient = new OSSClientBuilder().build(aliOss.getEndpoint(), aliOss.getAccessKeyId(), aliOss.getAccessKeySecret());
            InputStream input = null;
            try {
                input = new ByteArrayInputStream(bytes);
                ossClient.putObject(new PutObjectRequest(aliOss.getBucketName(), filePath, input));
            } catch (Exception e) {
                e.printStackTrace();
                System.err.println(e.getMessage());
            } finally {
                ossClient.shutdown();
                if (Objects.nonNull(input)) {
                    input.close();
                }
            }
            return;
        }

        QCloud qCloud = shopConfig.getQCloud();
        if (qCloud.getIsOpen()) {
            //初始化cosClient
            COSClient cosClient = cosClient(qCloud);
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength(bytes.length);
            objectMetadata.setContentType(extName);
            ByteArrayInputStream input = null;
            try {
                input = new ByteArrayInputStream(bytes);
                com.qcloud.cos.model.PutObjectRequest putObjectRequest =
                        new com.qcloud.cos.model.PutObjectRequest(qCloud.getBucketName(), filePath, input, objectMetadata);
                cosClient.putObject(putObjectRequest);
            } catch (Exception e) {
                e.printStackTrace();
                System.err.println(e.getMessage());
            } finally {
                cosClient.shutdown();
                if (Objects.nonNull(input)) {
                    input.close();
                }
            }
        }
    }

    private String randomFilePath(String fileType) {
        return DateUtil.format(new Date(), NORM_MONTH_PATTERN) + IdUtil.simpleUUID() + "." + fileType;
    }

    @Override
    public void deleteFile(String filePath) {
        attachFileMapper.delete(new LambdaQueryWrapper<AttachFile>().eq(AttachFile::getFilePath, filePath));
        try {
            Qiniu qiniu = shopConfig.getQiniu();
            if (qiniu.getIsOpen()) {
                bucketManager(qiniu).delete(qiniu.getBucket(), filePath);
                return;
            }

            AliOss aliOss = shopConfig.getAliOss();
            if (aliOss.getIsOpen()) {
                OSS ossClient = new OSSClientBuilder().build(aliOss.getEndpoint(), aliOss.getAccessKeyId(), aliOss.getAccessKeySecret());
                try {
                    ossClient.deleteObject(aliOss.getBucketName(), filePath);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.err.println(e.getMessage());
                } finally {
                    ossClient.shutdown();
                }
                return;
            }

            QCloud qCloud = shopConfig.getQCloud();
            if (qCloud.getIsOpen()) {
                COSClient cosClient = cosClient(qCloud);
                try {
                    cosClient.deleteObject(qCloud.getBucketName(), filePath);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.err.println(e.getMessage());
                } finally {
                    cosClient.shutdown();
                }
            }
        } catch (QiniuException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Boolean updateFile(AttachFile attachFile) {
        return attachFileMapper.updateFile(attachFile);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String uploadImFile(byte[] bytes, String originalName) throws IOException {
        String extName = FileUtil.extName(originalName);
        String imgName = FileUtil.mainName(originalName);
        // 聊天文件用一个新的文件夹装起来
        String filePath = randomFilePath("im/" + extName);
        AttachFile attachFile = getAttachFile(bytes, extName, imgName, filePath);
        attachFileMapper.insert(attachFile);

        upload(bytes, filePath, extName);
        return filePath;
    }

    @Override
    public Long uploadPlatformFile(byte[] bytes, String originalName, AttachFile attachFile) throws IOException {
        String extName = FileUtil.extName(originalName);
        String imgName = FileUtil.mainName(originalName);
        String filePath = randomFilePath(extName);
        attachFile.setFilePath(filePath);
        attachFile.setFileSize(bytes.length);
        attachFile.setFileType(extName);
        attachFile.setFileName(imgName);
        attachFile.setUploadTime(new Date());
        attachFileMapper.insert(attachFile);
        upload(bytes, filePath, extName);
        return attachFile.getFileId();
    }

    @Override
    public void deleteByIdsAndShopId(List<Long> ids) {
        List<AttachFile> attachFileList = attachFileMapper.getByIds(ids);
        // 获取文件的实际路径--数据库中保存的文件路径为： / + 实际的文件路径
        List<String> filePaths = attachFileList.stream().map(AttachFile::getFilePath).collect(Collectors.toList());
        this.batchDeleteFiles(filePaths);
        attachFileMapper.batchDeleteByIds(ids);
    }

    @Override
    public void batchMoveByShopIdAndIdsAndGroupId(List<Long> ids, Long groupId) {
        if (Objects.isNull(groupId)) {
            return;
        }
        AttachFileGroup attachFileGroup = attachFileGroupService.getByAttachFileGroupId(groupId);
        if (Objects.isNull(attachFileGroup)) {
            throw new KimoShopBindException("当前分组已不存在，请刷新后重试");
        }
        attachFileMapper.batchMoveByShopIdAndIdsAndGroupId(ids, groupId);
    }

    /**
     * 根据文件路径批量删除文件
     *
     * @param filePaths 文件路径列表
     */
    private void batchDeleteFiles(List<String> filePaths) {
        //图片添加也是循环添加，批量删除目前也是循环单个删除，后续或许有更好的办法
        for (String filePath : filePaths) {
            deleteFile(filePath);
        }
    }

    private AttachFile getAttachFile(byte[] bytes, String extName, String imgName, String filePath) {
        AttachFile attachFile = new AttachFile();
        attachFile.setFilePath(filePath);
        attachFile.setFileSize(bytes.length);
        attachFile.setFileType(extName);
        attachFile.setFileName(imgName);
        attachFile.setUploadTime(new Date());
        return attachFile;
    }

    // ================== 七牛云配置 ======================

    /**
     * 构建一个七牛上传工具实例
     */
    private UploadManager uploadManager(Qiniu qiniu) {
        return new UploadManager(qiniuConfig(qiniu));
    }

    /**
     * 认证信息实例
     *
     * @return
     */
    private Auth auth(Qiniu qiniu) {
        return Auth.create(qiniu.getAccessKey(), qiniu.getSecretKey());
    }

    /**
     * 构建七牛空间管理实例
     */
    private BucketManager bucketManager(Qiniu qiniu) {
        return new BucketManager(auth(qiniu), qiniuConfig(qiniu));
    }

    /**
     * 根据配置文件选择机房
     */
    private com.qiniu.storage.Configuration qiniuConfig(Qiniu qiniu) {

        Zone zone = null;
        if (Objects.equals(qiniu.getZone(), QiniuZone.HUA_BEI)) {
            zone = Zone.huabei();
        } else if (Objects.equals(qiniu.getZone(), QiniuZone.HUA_DONG)) {
            zone = Zone.huadong();
        } else if (Objects.equals(qiniu.getZone(), QiniuZone.HUA_NAN)) {
            zone = Zone.huanan();
        } else if (Objects.equals(qiniu.getZone(), QiniuZone.BEI_MEI)) {
            zone = Zone.beimei();
        } else if (Objects.equals(qiniu.getZone(), QiniuZone.XIN_JIA_PO)) {
            zone = Zone.xinjiapo();
        }
        return new com.qiniu.storage.Configuration(zone);
    }

    /**
     * 创建腾讯云cos client
     *
     * @return
     */
    private COSClient cosClient(QCloud qCloud) {
        BasicCOSCredentials cred = new BasicCOSCredentials(qCloud.getSecretId(), qCloud.getSecretKey());
        Region region = new com.qcloud.cos.region.Region(qCloud.getRegion());
        ClientConfig clientConfig = new ClientConfig(region);
        //https协议
        clientConfig.setHttpProtocol(HttpProtocol.https);
        return new COSClient(cred, clientConfig);
    }

}
